package com.sl.ms.carriage.service.impl;


import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sl.ms.base.api.common.AreaFeign;
import com.sl.ms.carriage.domain.constant.CarriageConstant;
import com.sl.ms.carriage.domain.dto.CarriageDTO;
import com.sl.ms.carriage.domain.dto.WaybillDTO;
import com.sl.ms.carriage.domain.enums.EconomicRegionEnum;
import com.sl.ms.carriage.entity.CarriageEntity;
import com.sl.ms.carriage.enums.CarriageExceptionEnum;
import com.sl.ms.carriage.mapper.CarriageMapper;
import com.sl.ms.carriage.service.CarriageService;
import com.sl.ms.carriage.utils.CarriageUtils;
import com.sl.transport.common.exception.SLException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class CarriageServiceImpl extends ServiceImpl<CarriageMapper, CarriageEntity> implements CarriageService{

    @Resource
    private AreaFeign areaFeign;

    /**
     * 获取全部运费模版
     * @return 运费模版对象列表
     */
    @Override
    public List<CarriageDTO> findAll() {

        // 构造查询条件
        LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.<CarriageEntity>lambdaQuery()
                // 按创建时间倒序
                .orderByDesc(CarriageEntity::getCreated);
        // 查询数据库
        List<CarriageEntity> list = super.list(queryWrapper);

        // 转化对象，返回集合数据
//        return CollStreamUtil.toList(list,carriageEntity -> {
//            CarriageDTO carriageDTO = BeanUtil.toBean(carriageEntity, CarriageDTO.class);
//            // 关联城市数据按照逗号分割成集合
//            carriageDTO.setAssociatedCityList(StrUtil.split(carriageEntity.getAssociatedCity(),','));
//            return carriageDTO;
//        });

        // 优化代码 CarriageUtils工具类
        return CollStreamUtil.toList(list,CarriageUtils::toDTO);


    }

    /**
     * 新增/修改运费模版
     * @param carriageDTO 新增/修改运费对象
     * @return
     */
    @Override
    @Transactional
    public CarriageDTO saveOrUpdate(CarriageDTO carriageDTO) {
        // 1.设置查询条件，查询运费模版列表
        LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.<CarriageEntity>lambdaQuery()
                // 判断模版类型是否相等
                .eq(CarriageEntity::getTemplateType, carriageDTO.getTemplateType())
                // 判断运费类型是否是 普快
                .eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST);

        // 该函数使用 MyBatis Plus 的 list 方法，通过 queryWrapper 条件查询数据库，并返回一个 List<CarriageEntity> 类型的结果列表。
        // super.list：调用父类的 list 方法。queryWrapper：提供查询条件。
        List<CarriageEntity> carriageList = super.list(queryWrapper);

        // 2.判断是否为经济区互寄，如果不是：需要进一步判断是否重复，是：判断关联城市是否重复
        // 判断：如果传入的模版类型 和 经济区互寄 不相等 ===> 不是经济区 ===> 判断模版是否存在
        if (ObjectUtil.notEqual(carriageDTO.getTemplateType(),CarriageConstant.ECONOMIC_ZONE)){
            // 判断模版是否存在：判断查询到的集合carriageList是否为空
            // 模版集合不为空 且 是新增操作(通过传入的id carriageDTO.getId()为空) ===> 抛异常
            if (CollUtil.isNotEmpty(carriageList) && ObjectUtil.isEmpty(carriageDTO.getId())){
                // 说明数据库中，改类型的模版已经存在，并且此次的操作位新增，数据重复
                throw new SLException(CarriageExceptionEnum.NOT_ECONOMIC_ZONE_REPEAT);
            }
            // TODO：存在bug，修改模版的时候，修改成其他的模版的名字，也可以修改成功 相当于新增
            // 把集合中的自己排除掉，不处理自己的那条数据，看有没有数据，集合中有数据：模版重复；只是有自己那一条数据，说明是修改
            // 修改：只能修改 除模版类型 以外的参数
            // 更新时判断是否已经存在该类型的模版
            long count = StreamUtil.of(carriageList)
                    .filter(carriageEntity -> ObjectUtil.notEqual(carriageEntity.getId(), carriageDTO.getId()))
                    .count();
            if (count > 0){
                throw new SLException(CarriageExceptionEnum.NOT_ECONOMIC_ZONE_REPEAT);
            }

            // 不是经济区，模版存在/不存在，不是新增操作
            // 新增/更新模版操作
            return this.saveOrUpdateCarriage(carriageDTO);
        }

        // 3.是经济区互寄：需要判断关联城市是否有重复
        if (CollUtil.isEmpty(carriageList)){
            // 目前数据库中不存在经济区互寄模版，直接保存即可
            return this.saveOrUpdateCarriage(carriageDTO);
        }

        // 目前数据库中存在经济区互寄模版：需要关联的城市是否有重复，如果有重复抛出异常
        // 判断重复的思路：先将查询出的运费模版中关联城市收集起来，传入的关联城市是否在此集合中
        // 查询其他模版中所有的经济区列表

        // 关联城市可能会存在2,3 情况 共用一个模版，判断传入的对象和已存在的 的关联城市是否一样
        // 提取出所有用到的关联城市编号  遍历 StreamUtil.of()
        List<String> associatedCityList = StreamUtil.of(carriageList)
                // TODO
                // 更新操作：需要先排除自己，再检查是否与其他模版重复
                // 把自己排除掉，非自己的保留下来
                // 过滤掉 与自己不相等？？？，如果是新增：carriageDTO.getId()为空 ==>不相等 ==> 没有过滤
                .filter(carriageEntity -> ObjectUtil.notEqual(carriageEntity.getId(), carriageDTO.getId()))
                // 获取关联城市的字符串
                .map(CarriageEntity::getAssociatedCity)
                // 将关联城市按照逗号分割,得到一个个集合
                .map(associatedCity -> StrUtil.split(associatedCity, ','))
                // 把上面得到的集合全部展开,得到一个一个的字符串数据
                .flatMap(StreamUtil::of)
                // 收集到集合中
                .collect(Collectors.toList());

        // 判断 查询到的集合associatedCityList 与 CarriageDTO中的 associatedCityList集合是否有交集
        Collection<String> intersection = CollUtil.intersection(associatedCityList, carriageDTO.getAssociatedCityList());
        // 取交集，如果存在交集说明重复
        if(CollUtil.isNotEmpty(intersection)){
            throw new SLException(CarriageExceptionEnum.ECONOMIC_ZONE_CITY_REPEAT);
        }

        // 不重复
        return this.saveOrUpdateCarriage(carriageDTO);
    }

    /**
     * 新增/修改运费模版
     * @param carriageDTO 传入的信息
     * @return
     */
    private CarriageDTO saveOrUpdateCarriage(CarriageDTO carriageDTO){
        // 将carriageDTO转成实体
        CarriageEntity carriageEntity = CarriageUtils.toEntity(carriageDTO);
        // 新增/修改，返回true 或 false
        boolean result = super.saveOrUpdate(carriageEntity);
        if (result){
            return CarriageUtils.toDTO(carriageEntity);
        }
        // 没有新增/更新成功
        throw new SLException(CarriageExceptionEnum.SAVE_OR_UPDATE_ERROR);
    }

    /**
     * 运费计算
     * @param waybillDTO 运费计算对象
     * @return 运费模版对象，不仅包含模版数据还包含：computeWeight 、expense 字段
     */
    @Override
    public CarriageDTO compute(WaybillDTO waybillDTO) {
        // 1.根据参数查找运费模版
        CarriageEntity carriage = this.findCarriage(waybillDTO);

        // 2.计算出实际计费重量，确保最小重量为1kg
        double computeWeight = this.getComputeWeight(waybillDTO,carriage);

        //3. 计算运费，首重 + 续重，保留一位小数
        double expense = carriage.getFirstWeight() + ((computeWeight - 1) * carriage.getContinuousWeight());
        expense = NumberUtil.round(expense, 1).doubleValue();

        //4. 封装对象返回
        CarriageDTO carriageDTO = CarriageUtils.toDTO(carriage);
        carriageDTO.setExpense(expense);
        carriageDTO.setComputeWeight(computeWeight);
        return carriageDTO;
    }

    private CarriageEntity findCarriage(WaybillDTO waybillDTO) {
        //1. 校验是否为同城
        if (ObjectUtil.equals(waybillDTO.getReceiverCityId(), waybillDTO.getSenderCityId())) {
            //同城
            CarriageEntity carriageEntity = this.findByTemplateType(CarriageConstant.SAME_CITY);
            if (ObjectUtil.isNotEmpty(carriageEntity)) {
                return carriageEntity;
            }
        }
        // 2.校验是否为省内
        // 2.1 获取收寄件地址省份id
        Long receiverProvinceId = this.areaFeign.get(waybillDTO.getReceiverCityId()).getParentId();
        Long senderProvincedId = this.areaFeign.get(waybillDTO.getSenderCityId()).getParentId();
        if (ObjectUtil.equal(receiverProvinceId,senderProvincedId)){
            // 2.2 查询同省运费模版
            CarriageEntity carriageEntity = this.findByTemplateType(CarriageConstant.SAME_PROVINCE);
            if (ObjectUtil.isNotEmpty(carriageEntity)){
                return carriageEntity;
            }
        }

        // 3.校验是否为经济区互寄
        // 3.1 获取经济区城市配置枚举
        LinkedHashMap<String, EconomicRegionEnum> EconomicRegionMap = EnumUtil.getEnumMap(EconomicRegionEnum.class);
        EconomicRegionEnum economicRegionEnum = null;
        for (EconomicRegionEnum regionEnum : EconomicRegionMap.values()){
            // 该经济区是否全部包含收发件省id
            boolean result = ArrayUtil.containsAll(regionEnum.getValue(),receiverProvinceId,senderProvincedId);
            if (result){
                economicRegionEnum = regionEnum;
                break;
            }
        }
        if (ObjectUtil.isNotEmpty(economicRegionEnum)){
            // 3.2 根据类型编码查询
            LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.lambdaQuery(CarriageEntity.class)
                    .eq(CarriageEntity::getTransportType, CarriageConstant.ECONOMIC_ZONE)
                    .eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST)
                    .like(CarriageEntity::getAssociatedCity, economicRegionEnum.getCode());

            CarriageEntity carriageEntity = super.getOne(queryWrapper);
            if (ObjectUtil.isNotEmpty(carriageEntity)){
                return carriageEntity;
            }
        }
        // 4.跨省模版
        return this.findByTemplateType(CarriageConstant.TRANS_PROVINCE);
    }

    private double getComputeWeight(WaybillDTO waybillDTO,CarriageEntity carriage){
        // 计算体积，如果传入体积不需要计算
        Integer volume = waybillDTO.getVolume();
        if (ObjectUtil.isEmpty(volume)){
            try{
                // 长 * 宽 * 高计算体积
                volume = waybillDTO.getMeasureLong() * waybillDTO.getMeasureWidth() * waybillDTO.getMeasureHigh();
            } catch (Exception e){
                // 计算出错设置体积0
                volume = 0;
            }
        }
        // 计算体重重量，体积 / 轻抛系数
        BigDecimal volumeWeight = NumberUtil.div(volume, carriage.getLightThrowingCoefficient(), 1);

        // 取大值
        double computeWeight = NumberUtil.max(volumeWeight.doubleValue(),NumberUtil.round(waybillDTO.getWeight(),1).doubleValue());

        // 计算续重，规则：不满1kg,按1kg计费；10kg以下续重以0.1kg计量保留1位小数；10-100kg续重以0.5kg计量保留1位小数；100kg以上四舍五入取整数
        if (computeWeight <= 1){
            return 1;
        }

        if (computeWeight <= 10){
            return computeWeight;
        }

        // 举例：
        // 108.4kg按照108kg收费
        // 108.5kg按照109kg收费
        // 108.6kg按照109kg收费
        if (computeWeight >= 100){
            return NumberUtil.round(computeWeight,0).doubleValue();
        }

        //0.5为一个计算单位，举例：
        // 18.8kg按照19收费，
        // 18.4kg按照18.5kg收费
        // 18.1kg按照18.5kg收费
        // 18.6kg按照19收费
        int integer = NumberUtil.round(computeWeight, 0, RoundingMode.DOWN).intValue();
        if (NumberUtil.sub(computeWeight,integer) == 0){
            return integer;
        }
        if (NumberUtil.sub(computeWeight,integer) <= 0.5){
            return NumberUtil.add(integer,0.5);
        }
        return NumberUtil.add(integer,1);
    }



    /**
     *
     * @param templateType 模板类型：1-同城寄，2-省内寄，4-跨省
     * @return
     */
    @Override
    public CarriageEntity findByTemplateType(Integer templateType) {
        if (ObjectUtil.equal(templateType,CarriageConstant.ECONOMIC_ZONE)){ // 经济区互寄
            throw new SLException(CarriageExceptionEnum.METHOD_CALL_ERROR);
        }
        LambdaQueryWrapper<CarriageEntity> queryWrapper = Wrappers.lambdaQuery(CarriageEntity.class)
                .eq(CarriageEntity::getTransportType, CarriageConstant.REGULAR_FAST);
        return super.getOne(queryWrapper);
    }

}
